package edu.cmsc611.textsimulator;

import java.util.*;

public class ICacheManager {

	/*
	 * Instruction cache memory size=16, with 4 blocks and 4 words per block
	 * Instruction will be stored to memory starting address 0X00 and will be increased by 4
	 * So if it start from 0, then 0,4,8,12,16 .... etc
	 * So program counter will start from 0 and everytime will increase the address by 4.
	 * if we use the array then we can take the 4 as one and if pc is send, then pc/4;
	 * for cirulating, we use the module cachesize;
	 */
	private static ICacheManager instance;
	private static int cachesize=16;
	private boolean currentloadfinished;
	private int currentloadElapseCycle=0;
	private int recLoadCycle=0;

	
	LRUCache lrucache;

	private static Object monitor = new Object();
	//private Map<String, Object> cache = Collections.synchronizedMap(new HashMap<String, Object>());
	private String cache[]=new String[16];

	/**
	 * @return the currentloadfinished
	 */
	public boolean isCurrentloadfinished() {
		return currentloadfinished;
	}

	/**
	 * @param currentloadfinished the currentloadfinished to set
	 */
	public void setCurrentloadfinished(boolean currentloadfinished) {
		this.currentloadfinished = currentloadfinished;
	}

	/**
	 * @return the currentloadElapseCycle
	 */
	public int getCurrentloadElapseCycle() {
		return currentloadElapseCycle;
	}

	/**
	 * @param currentloadElapseCycle the currentloadElapseCycle to set
	 */
	public void setCurrentloadElapseCycle(int currentloadElapseCycle) {
		this.currentloadElapseCycle = currentloadElapseCycle;
	}


	private ICacheManager() {
		FunctionalUnitSingleton funit=FunctionalUnitSingleton.getInstance();
		for(int i=0;i<cachesize;i++)
			cache[i]="";

		recLoadCycle=funit.mainMemAccesstime+funit.iCacheAccessTime;
		recLoadCycle=2*recLoadCycle;
		currentloadfinished=true;

	}

	public void load(int address, String instruction) {

	}

	public boolean search(int programcounter,String instructionstring,int instructionId,Vector instructions) {
		/*
		 * check the current pc to see if the instruction exists if not start loading
		 */
		lrucache=LRUCache.getInstance();
		Instruction inst;
		if(currentloadfinished){
			if(cache[(programcounter)%cachesize].equals(instructionstring))
			{
				lrucache.setInsCacheHitOccures(lrucache.getInsCacheHitOccures()+1);
				return true;
			}
			else
			{
				lrucache.setInsCacheMissOccures(lrucache.getInsCacheMissOccures()+1);
				currentloadElapseCycle=1;
				for(int i=0;i<4;i++)
				{
					/*
					 * if remain instruciton number is less than 4, then load whatever it has left
					 */
					if((instructionId+i)<instructions.size()){

					inst=(Instruction)instructions.elementAt(instructionId+i);
					cache[((programcounter+i))%cachesize]=inst.instructionString;
					}
				}
				currentloadfinished=false;
				return false;
			}
		}else
		{
			currentloadElapseCycle++;
			if(currentloadElapseCycle==recLoadCycle){
				currentloadfinished=true;
				return true;
			}
			else
				return false;
//			for(int i=0;i<4;i++)
//			{
//				inst=(Instruction)instructions.elementAt(instructionId+i);
//				cache[((programcounter+i)/4)%cachesize]=inst.instructionString;
//			}
		}
	}

	public void clear(int cacheKey) {
		cache[cacheKey]="";
	}

	public void clear() {
		for(int address=0;address<cachesize;address++)
			cache[0]="";
	}

	public static ICacheManager getInstance() {
		if (instance == null) {
			synchronized (monitor) {
				if (instance == null) {
					instance = new ICacheManager();
				}
			}
		}
		return instance;
	}

}